:- use_module('abc_parser.yap').
:- use_module(library(lists)).

print_abc_file(ABC) :-
		ABC = abc(MinV,MajV,CP,MC,Methods,MetaC,Metas,CC,Instances,Classes,
		SC,Scripts,BC,MB),
	
	format('MinV=~w,MajV=~w~n~n', [MinV,MajV]),
	CP = cpoolinfo(IntCount,Integers,UIntCount,UIntegers,DoubleCount,
			Doubles,StringCount,Strings,NamespaceCount,
			Namespaces,NSSetCount,NSSets,MultinameCount,Multinames),
	print_arraycount(Integers,IntCount,'Integers'),
	print_arraycount(UIntegers,UIntCount,'UIntegers'),
	print_arraycount(Doubles,DoubleCount,'Doubles'),
	print_arraycount(Strings,StringCount,'Strings'),
	print_arraycount(Namespaces,NamespaceCount,'Namespaces'),
	print_arraycount(NSSets,NSSetCount,'NSSets'),
	print_arraycount(Multinames,MultinameCount,'Multinames'),
	
	print_arraycount(Methods,MC,'Methods'),
	print_arraycount(Metas,MetaC,'Metas'),
	print_arraycount(Instances,CC,'Instances'),
	print_arraycount(Classes,CC,'Classes'),
	print_arraycount(Scripts,SC,'Scripts'),

	writeln('\n\n'),
	print_method_bodies(MB).
	

print_arraycount(Array, Count, String) :-
       write(String),
       write('Count is:'),
       write(Count),
       write('\n'),
       write('printing array:\n'),
       help_print_arrays(Array, Array),
       write('---------------------------\n').
	
help_print_arrays([A|As], FullArray) :- 
		nth0(N,FullArray,A),
		write(N),
		write('  '),
		write(A), write('\n'), help_print_arrays(As, FullArray).
help_print_arrays([],_) :- write('').

print_code([]).
print_code([I|Is]) :-
	writeln(I),
	print_code(Is).
print_method_bodies([]).
print_method_bodies([MB|MBs]) :-
	print_method_body(MB),
	write('\n'),
	print_method_bodies(MBs).
print_method_body(method_body(Method,MS,LC,ISD,MSD,CL,C,EC,E,TC,T)) :-
	format('Method is ~w,', [Method]),
	format('MS is ~w,', [MS]),
	format('LC is ~w,', [LC]),
	format('ISD is ~w,', [ISD]),
	format('MSD is ~w,', [MSD]),
	format('CL is ~w,', [CL]),
	
	% format('C is ~w,', [C]),
	format('EC is ~w,', [EC]),
	format('E is ~w,', [E]),
	format('TC is ~w,', [TC]),
	format('T is ~w~n~n', [T]),
	print_code(C).

calculate_code_length([],L,L).
calculate_code_length([_O-I|Rest],OldL,NewL) :-
	instruction_size(I,Size),
	UpdatedL is OldL+Size,
	calculate_code_length(Rest,UpdatedL,NewL).


find_byte_length(Value, Length) :-
	abc_parser:u30Int_rev(Bytes, Value),
	length(Bytes, Length).
	

instruction_size(add,1).
instruction_size(add_i,1).
instruction_size(applytype(ArgCount),Sum) :- find_byte_length(ArgCount,L), Sum is L+1.
instruction_size(astype(I),Sum) :- find_byte_length(I,L), Sum is L+1.
instruction_size(astypelate,1).
instruction_size(bitand,1).
instruction_size(bitnot,1).
instruction_size(bitor,1).
instruction_size(bitxor,1).
instruction_size(call(ArgCount),Sum) :- find_byte_length(ArgCount,L), Sum is L+1.
instruction_size(callmethod(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callproperty(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callproplex(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callpropvoid(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callstatic(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callsuper(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(callsupervoid(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(checkfilter,1).
instruction_size(coerce(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(coerce_a,1).
%TODO: don't know how to handle the following.
%instruction_size(coerce_i(Index),L+1) --> [0x83], find_byte_length(Index,L).
instruction_size(coerce_s,1).
instruction_size(construct(ArgCount),Sum) :- find_byte_length(ArgCount,L), Sum is L+1.
instruction_size(constructprop(Index,ArgCount),Sum) :- 
		find_byte_length(Index,L1), 
		find_byte_length(ArgCount,L2),
		Sum is (L1 + L2 + 1).
instruction_size(constructsuper(ArgCount),Sum) :- find_byte_length(ArgCount,L), Sum is L+1.
instruction_size(convert_b,1).
instruction_size(convert_i,1).
instruction_size(convert_d,1).
instruction_size(convert_o,1).
instruction_size(convert_u,1).
instruction_size(convert_s,1).
instruction_size(debug(DebugType,Index,Reg,Extra),L1+L2+3) :-
	find_byte_length(Index,L1), 
	find_byte_length(Extra,L2).
instruction_size(debugfile(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(debugline(Linenum),Sum) :- find_byte_length(Linenum,L), Sum is L+1.
instruction_size(declocal(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(declocal_i(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(decrement,1).
instruction_size(decrement_i,1).
instruction_size(deleteproperty(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(divide,1).
instruction_size(dup,1).
instruction_size(dxns(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(dxnslate,1). 
instruction_size(equals,1).
instruction_size(esc_xattr,1).
instruction_size(esc_xelem,1).
instruction_size(findproperty(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(findpropstrict(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getdescendants(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getglobalscope,1).
instruction_size(getglobalslot(SlotIndex),Sum) :- find_byte_length(SlotIndex,L), Sum is L+1.
instruction_size(getlex(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getlocal(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getlocal_0,1).
instruction_size(getlocal_1,1).
instruction_size(getlocal_2,1).
instruction_size(getlocal_3,1).
instruction_size(getproperty(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getscopeobject(_Index),2).
instruction_size(getslot(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(getsuper(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(greaterequals,1).
instruction_size(greaterthan,1).
instruction_size(hasnext,1).
instruction_size(hasnext2(ObjReg,IndexReg),Sum) :- 
		find_byte_length(ObjReg,L1), 
		find_byte_length(IndexReg,L2),
		Sum is (L1 + L2 + 1).
instruction_size(ifeq(_Offset),4).
instruction_size(iffalse(_Offset),4).
instruction_size(ifge(_Offset),4).
instruction_size(ifgt(_Offset),4).
instruction_size(ifle(_Offset),4).
instruction_size(iflt(_Offset),4).
instruction_size(ifnge(_Offset),4).
instruction_size(ifngt(_Offset),4).
instruction_size(ifnle(_Offset),4).
instruction_size(ifnlt(_Offset),4).
instruction_size(ifne(_Offset),4).
instruction_size(ifstricteq(_Offset),4).
instruction_size(ifstrictne(_Offset),4).
instruction_size(iftrue(_Offset),4).
instruction_size(in,1).
instruction_size(inclocal(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(inclocal_i(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(increment,1).
instruction_size(increment_i,1).
instruction_size(initproperty(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(instanceof,1).
instruction_size(istype(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(istypelate,1).
instruction_size(jump(_Offset),4).
instruction_size(kill(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(label,1).
instruction_size(lessequals,1).
instruction_size(lessthan,1).
instruction_size(lookupswitch(DefaultOffset,CaseCount,CaseOffsets),L) :-
	find_byte_length(CaseCount,CL),
	Count is CaseCount+1,
	length(CaseOffsets,Count),
	L is ((Count*3)+4+CL).
%instruction_size(lookupswitch(_,_,_),20).
instruction_size(lshift,1).
instruction_size(modulo,1).
instruction_size(multiply,1).
instruction_size(multiply_i,1).
instruction_size(negate,1).
instruction_size(negate_i,1).
instruction_size(newactivation,1).
instruction_size(newarray(ArgCount),Sum) :- find_byte_length(ArgCount,L), Sum is L+1.
instruction_size(newcatch(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(newclass(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(newfunction(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(newobject(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(nextname,1).
instruction_size(nextvalue,1).
instruction_size(nop,1).
instruction_size(not,1).
instruction_size(pop,1).
instruction_size(popscope,1).
instruction_size(pushbyte(_Byte),2).
instruction_size(pushdouble(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(pushfalse,1).
instruction_size(pushint(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(pushnamespace(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(pushnan,1).
instruction_size(pushnull,1).
instruction_size(pushscope,1).
instruction_size(pushshort(Value),Sum) :- find_byte_length(Value,L), Sum is L+1.
instruction_size(pushstring(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(pushtrue,1).
instruction_size(pushuint(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(pushundefined,1).
%instruction_size(pushuninitialized(Byte),2) --> [0x22], [Byte].
instruction_size(pushwith,1).
instruction_size(returnvalue,1).
instruction_size(returnvoid,1).
instruction_size(rshift,1).
instruction_size(setlocal(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(setlocal_0,1).
instruction_size(setlocal_1,1).
instruction_size(setlocal_2,1).
instruction_size(setlocal_3,1).
instruction_size(setglobalslot(SlotIndex),Sum) :- find_byte_length(SlotIndex,L), Sum is L+1.
instruction_size(setproperty(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(setslot(SlotIndex),Sum) :- find_byte_length(SlotIndex,L), Sum is L+1.
instruction_size(setsuper(Index),Sum) :- find_byte_length(Index,L), Sum is L+1.
instruction_size(strictequals,1).
instruction_size(subtract,1).
instruction_size(subtract_i,1).
instruction_size(swap,1).
instruction_size(throw,1).
instruction_size(typeof,1).
instruction_size(urshift,1).

%------------> UTILITY PREDICATES

find_index_namespaceCP(String, CP, ProtectedNSIndex) :-
	CP =.. CPList,
	nth(9, CPList, Strings),
	nth(11, CPList, NameSpaces),
	%writeln(NameSpaces),

	find_index_stringCP(String, Strings, StringIndex),
	identify_namespaces(NameSpaces,StringIndex,0,ProtectedNSIndex).

identify_namespaces([N|_Rest], StringIndex, OldIndex, NewIndex) :-
	N =.. [namespace_info, _Kind, Name],
	StringIndex = Name,
	NewIndex is OldIndex + 1.
identify_namespaces([N|Rest], StringIndex, OldIndex, NewIndex) :-
	N =.. [namespace_info, _Kind, Name],
	StringIndex \= Name,
	UpdatedOldIndex is OldIndex + 1,
	identify_namespaces(Rest, StringIndex, UpdatedOldIndex, NewIndex).
identify_namespaces([], _StringIndex, _OldIndex, -1) :-
	write('The string you are searching for in the namespaces array is not present').


%--------------------------------------
%find_index_multinameCP
%looks up that String in the constant pool, and finds out which
%MultinameIndex is associated with that String in the Multiname Array.
%
find_index_multinameCP(String, CP, MultinameIndex) :-
	CP =.. CPList,
	nth(9, CPList, Strings),
	nth(15, CPList, Multinames),
	find_index_stringCP(String, Strings, StringIndex),
	identify_multinames(Multinames, StringIndex, 0, MultinameIndex).


identify_multinames([M|Rest], StringIndex, OldIndex, NewIndex) :-
	%format('case 1, M=~w~n', [M]),
	M =.. [m_info, _Type],
	UpdatedOldIndex is OldIndex + 1,
	identify_multinames(Rest, StringIndex, UpdatedOldIndex, NewIndex).
identify_multinames([M|Rest], StringIndex, OldIndex, NewIndex) :-
	%format('case 2, M=~w~n', [M]),
	M =.. [m_info, _Type, Data],
	%write(Data),
	Data =.. [_MultinameType, _],
	UpdatedOldIndex is OldIndex + 1,
	identify_multinames(Rest, StringIndex, UpdatedOldIndex, NewIndex).
identify_multinames([M|Rest], StringIndex, OldIndex, NewIndex) :-
	%format('case 3, M=~w~n', [M]),
	M =.. [m_info, _Type, Data],
	%write(Data),
	Data =.. [MultinameType, _, _],
	MultinameType = multiname,
	UpdatedOldIndex is OldIndex + 1,
	identify_multinames(Rest, StringIndex, UpdatedOldIndex, NewIndex).
identify_multinames([M|Rest], StringIndex, OldIndex, NewIndex) :-
	%format('case 4, M=~w~n', [M]),
	M =.. [m_info, _Type, Data],
	%write(Data),
	Data =.. [MultinameType, _NS, Name],
	MultinameType = qname,
	StringIndex \= Name,
	UpdatedOldIndex is OldIndex + 1,
	identify_multinames(Rest, StringIndex, UpdatedOldIndex, NewIndex).
identify_multinames([M|_Rest], StringIndex, OldIndex, NewIndex) :-
	%format('case 5, M=~w~n', [M]),
	M =.. [m_info, _Type, Data],
	%write(Data),
	Data =.. [MultinameType, _NS, Name],
	MultinameType = qname,
	StringIndex = Name,
	NewIndex is OldIndex + 1.
identify_multinames([M|Rest],StringIndex,OldIndex,NewIndex) :-
	%writeln('case 7'),
	%format('M=~w,SI=~w,OI=~w,NI=~w',[M,StringIndex,OldIndex,NewIndex]),
	M =.. [m_info, _Type, Data],
	Data =.. [MultinameType, _Name, _ParamsLength, _Params],
	MultinameType = typename,
	UpdatedOldIndex is OldIndex + 1,
	identify_multinames(Rest, StringIndex,UpdatedOldIndex,NewIndex).
identify_multinames([], _StringIndex, _OldIndex, -1) :-
	%writeln('case 6'),
	write('The string you are searching for in the multiname index is not present').
%-----------------------------------

%Given a string, finds out its index in the string array.
find_index_stringCP(String, Strings, StringIndex) :-
	identify_string(Strings, String, 0, StringIndex).

identify_string([S|_Rest], IString, OldIndex, NewIndex) :-
	S =.. [string_info, _Size, _Chars, String],
	IString = String, 
	NewIndex is OldIndex + 1.
identify_string([S|Rest], IString, OldIndex, NewIndex) :-
	S =.. [string_info, _Size, _Chars, String],
	IString \= String,
	UpdatedOldIndex is OldIndex + 1,
	identify_string(Rest, IString, UpdatedOldIndex, NewIndex).
identify_string([], _IString, _OldIndex, -1).


%--------------------------------------
